#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <csignal>
#include <unistd.h>
#include "time.hpp"
const std::string path = "/home/";
const int projid = 0x6666;
// 操作系统申请空间是按照块为单位的，你申请不到两块，给你两块，但只让你用你申请的
const int gshmsize = 4096;

const mode_t mode = 0600;
std::string ToHex(key_t key)
{
    char buf[64];
    snprintf(buf, sizeof(buf), "%x", key);
    return buf;
}
class ShareMemory
{
    key_t k;
    int shmid;
    void *getd;
    int CreateSharedMemoryHelper(int flg)
    {
        // 1.创建key
        // ftok稳定：参数同，返回值同
        k = ::ftok(path.c_str(), projid); // 相同路径相同projid返回的是同一个key,方便用户进程找，只要约定好这两个，大部分情况都能生成唯一的key_t
        if (k < 0)
            return -1;
        // 2.server创建共享内存并获取
        // 共享内存是有权限的
        int shmid = ::shmget(k, gshmsize, flg); // 创建失败直接返回，不要创建失败了获取
        if (shmid < 0)
        {
            std::cerr << "shmget:" << std::endl;
            return -2;
        }
        return shmid;
    }

public:
    void *GetAddr()
    {
        return getd;
    }
    void CreateSharedM()
    {
        shmid = CreateSharedMemoryHelper(IPC_CREAT | IPC_EXCL | mode);
    }
    void GetSharedM()
    {
        shmid = CreateSharedMemoryHelper(IPC_CREAT);
    }
    void AttachM()
    {
        getd = shmat(shmid, nullptr, 0); // 默认//挂接失败可能没有对共享内存的权限
        if ((long long)getd == -1)
        {
            std::cerr << "Attach sharedmemory" << std::endl;
        }
    }
    void DeM()
    { // 解关联
        int ret = shmdt(getd);
        if (ret < 0)
            std::cerr << "shmdt" << std::endl;
    }
    void RemoveM()
    {
        int ret = shmctl(shmid, IPC_RMID, nullptr);
        if (ret < 0)
            std::cerr << "shmctl" << std::endl;
    }
};
ShareMemory shm;

// 传输完整数据块
struct Data
{
    char status[32];
    char lasttime[48];
    char image[1024];
};

enum{
    CREATENAMEPIPE=1<<1,
    EXITSNAMEPIPE=1<<2,
    OPENWITHWRITE=1<<3,
    OPENWITHREAD=1<<4
};
// 完整管道
class NamePipe
{
private:
    const char *pipepath = "/home/ubuntu/sharedmem/namepipe2";
    const mode_t mode = 0600;
    int fd=-1;
    char mesbuf[64];
public:
    char* WritePipe(const std::string&message)
    {
        if(fd==-1){
            std::cerr<<"hasn't open pipe"<<std::endl;
            return nullptr;
        }
        if(message.size()>sizeof(mesbuf)){
             std::cerr<<"too more code"<<std::endl;
            return nullptr;
        }
        int haswrite=::write(fd,message.c_str(),message.size());
        if(haswrite!=message.size())
        {
            std::cerr<<"::write error"<<strerror(errno)<<std::endl;
            return nullptr;
        }
        strncpy(mesbuf,message.c_str(),message.size());
        mesbuf[message.size()]=0;
        return mesbuf;
    }
    char* ReadPipe()
    {
        if(fd==-1){
            std::cerr<<"hasn't open pipe"<<std::endl;
            return nullptr;
        }
        int hasread=::read(fd,mesbuf,sizeof(mesbuf));
        if(hasread==-1)
        {
            std::cerr<<"::read error"<<strerror(errno)<<std::endl;
            return nullptr;
        }
        else if(hasread==0)
        {
            std::cout<<"written pipe was closed , i exit too"<<std::endl;
            exit(0);
        }
        mesbuf[hasread]=0;
        return mesbuf;
    }
    int OpenPipe_W()
    {
        return ::open(pipepath, O_WRONLY);
    }
    int OpenPipe_R()
    {
        return ::open(pipepath, O_RDONLY);
    }
    void ClosePipe(int _fd)
    {
        ::close(_fd);
    }
    void CreateNamePipe()
    {
        int ret = mkfifo(pipepath, mode);
        if (ret == -1)
        {
            std::cerr << "mkfifo:" << strerror(errno) << std::endl;
        }
    }
    NamePipe()=default;
    NamePipe(int mode)
    {
       //switch的case必须是常量值
       if(mode&CREATENAMEPIPE)
       {
            CreateNamePipe();
       }
       if(mode&OPENWITHWRITE)
       {
            fd=OpenPipe_W();
       }
       if(mode&OPENWITHREAD)
       {
            fd=OpenPipe_R();
       }
    }
    ~NamePipe()
    {
        ::unlink(pipepath);//从文件系统中删除一个文件名（硬链接）
        /*不影响已打开的文件描述符​​：
即使调用 unlink()，​​已打开该文件的进程仍能继续读写文件​​（通过文件描述符），直到所有文件描述符关闭后，文件才会被真正删除。
*/
    }
};